본문으로 건너뛰기

23.07.11

오늘 한 일

  • Gloddy 개발(상세 페이지)
  • TIL GA 재연결, Docsearch 연결
  • Rust 스터디 진행

추상화 수준

함수형 프로그래밍 공부를 하면서 배웠던 개념 중 계층형 설계가 있다.

추상화 벽을 통해 세부적인 것을 감추고, 추상화된 것을 통해 더 큰 것을 만들어 나가는 것이다.

함수형 프로그래밍 뿐만 아니라, 클린 코드 책에서도 이렇게 설명하고 있다.

노트

3장 함수

  • 작게 만들어라!
  • 한 가지만 해라!
  • 함수 당 추상화 수준은 하나로!

위에서 아래로 코드 읽기: 내려가기 규칙

코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 즉, 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.

핵심은 짧으면서도 '한 가지'만 하는 함수다. 위에서 아래로 TO 문단을 읽어내려 가듯이 코드를 구현하면 추상화 수준을 일관되게 유지하기가 쉬워진다.

이 개념을 프로젝트에 적용해보고 싶었다.

나는 이런 식으로 구분했다.

추상화 수준이 낮다는 것은 디테일이 많이 드러났다는 것

  • 높은 추상화 수준 - 페이지 컴포넌트
  • 보통 추상화 수준 - 섹션 컴포넌트
  • 낮은 추상화 수준 - 일반 컴포넌트

페이지 컴포넌트에서는 추상화된 컴포넌트를 가져와 페이지의 구조를 직관적으로 파악할 수 있도록 하고, 추상화된 컴포넌트 안에서는 세부적인 것을 구현하도록 하였다.

만약 한 컴포넌트에서 추상화 수준이 다르다면, 그 컴포넌트는 더 작은 컴포넌트로 분리해야 한다.

예시

다음은 진유림님의 토스ㅣSLASH 21 - 실무에서 바로 쓰는 Frontend Clean Code 에서 나왔던 예시다.

imageimage

Gloddy 프로젝트에서도 적용해보자

Gloddy 프로젝트에서도 이 개념을 적용해보았다.

export default function DetailPage({
params: { id },
}: {
params: {
id: string;
};
}) {
const { image, title, description, location, time } = DETAIL_DUMMY_DATA;

return (
<main>
<TopNavigationBar />
<TopSection id={id} title={title} thumbnailUrl={image} description={description} />
<div className="p-20 pb-100">
<MemberSection />
<Spacing size={18} />
<TimeSection time={time} />
<Spacing size={18} />
<LocationSection location={location} />
</div>
<ApplyButton />
</main>
);
}

이렇게 페이지 컴포넌트에서는 추상화된 컴포넌트를 가져와 페이지의 구조를 직관적으로 파악할 수 있도록 하였다.

Next13의 App Router를 사용하고 있는데 어떻게 서버 컴포넌트와 클라이언트 컴포넌트를 분리할 지 고민이었다.

위 방식대로 구현하면 페이지 컴포넌트를 서버 컴포넌트로 하고, 추상화된 컴포넌트는 상황에 따라 클라이언트 컴포넌트로 분리할 수 있을 것 같다.

Rust 스터디

8시 부터 9시 반까지 Rust 스터디를 진행했다.

String vs str

StringVec과 같은 dynamic heap string 타입이다.

str은 메모리 어딘가에 있는 가변 길이의 UTF-8 바이트로 구성된 불변 시퀀스이다.

크기를 알 수 없어서 포인터 뒤에서만 처리할 수 있다. 일반적으로 string slice 또는 그냥 slice라고 불리는 UTF-8 데이터에 대한 참조인 &str 타입으로 사용된다.

slice는 데이터를 읽는 용도로만 사용할 수 있기 때문에 해당 데이터는 어디에 있든지 상관없다.

노트

메모리 어딘가에 있다고 했는데, 상황에 따라 힙, 스택, 또는 바이너리에 있을 수 있다.

  • static storage: "hello world"라는 문자열 리터럴은 &'static str 타입이다. 이는 프로그램이 실행되는 동안에는 항상 메모리에 존재한다는 것을 의미한다.

  • heap에 할당된 String 내부: String은 String의 데이터에 대한 &str로 역참조된다.

fn takes_str(s: &str) { }

let s = String::from("Hello");

takes_str(&s);

String으로부터 &str가 만들어지고 takes_str 함수에 전달된다.

  • stack: 스택에 할당된 바이트 배열을 만들고 그 데이터를 가리키는 &str을 만들 수 있다.
use std::str;

let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();

참고

Rust: str vs String

What are the differences between Rust's String and str?


내일 할 일

  • CS 스터디 정리
  • Gloddy 개발
  • 오후 2시 데일리 스크럼
  • 오후 10시 30분 프로그라피 회의